home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
007
/
vtkerma2.arc
/
MSXIBM.ASM
< prev
next >
Wrap
Assembly Source File
|
1986-02-13
|
32KB
|
1,282 lines
PAGE 59, 132
TITLE MSXIBM -- Machine dependent module for IBM-PC, except Terminal
; Update 4 Jan 86
IF1
%OUT >> Starting pass 1
ELSE
%OUT >> Starting pass 2
ENDIF
PUBLIC SerIni_PC, SerRst_PC, ClrBuf_PC, OutChr_PC, Coms_PC
; PUBLIC VTS_PC
PUBLIC DoDel_PC, CtlU_PC, CmBlnk_PC, Locate_PC, PrtChr_PC, DoBaud_PC
PUBLIC ClearL_PC, DoDisk_PC, GetBaud_PC, Beep_PC
PUBLIC PutHlp_PC, PutMod_PC, PutErr_PC, ClrMod_PC, PosCur_PC
PUBLIC SendBr_PC, SetKTab_PC, SetKHlp_PC, LclIni_PC, ShowKey_PC
PUBLIC Which_card, Drop_DTR_PC, Which_page, Beam_Off, Beam_On
PUBLIC Set_up_video_card_pointers, CRT_Mode
INCLUDE msdefs.h
false EQU 0
true EQU 1
Screen EQU 10h ; BIOS screen call
XOFF_trigger_count EQU BufSiz*2 / 3 ; High point = 2/3 of buffer full
Mode_control_port EQU 3D8h ; CRT mode control port
Video_enable EQU 8 ; Display enable flag
Number_of_rows EQU 24 ; Length in rows
Number_of_columns EQU 80 ; Width in characters
Highest_row EQU Number_of_rows-1; Highest row number
Highest_column EQU Number_of_columns-1 ; Highest column number
Number_of_rows_PC EQU 25 ; Length in rows including mode line
; constants used by serial port handler
BRKBIT EQU 40h ; Send-break bit
TIMER EQU 40h ; Use to issue short beep
PORT_B EQU 61h ; Port B address
MCONF EQU 11h ; Machine configuration
KEYB EQU 16h
BIOS EQU 10h
MDMDAT1 EQU 03F8h ; Address of modem port 1 data register
MDMSTS1 EQU 03FDh ; Address of modem port 1 status register
MDMCOM1 EQU 03FBh ; Address of modem port 1 command register
MDMDAT2 EQU 02F8h ; Port 2 address register
MDMSTS2 EQU 02FDh ; Port 2 status register
MDMCOM2 EQU 02FBh ; Port 2 command register
MDMINP EQU 1 ; Input ready bit
MDMINTV EQU 0030h ; Address of modem port interrupt vector
MDINTV2 EQU 002Ch ; Address for port 2
MDMINTO EQU 0EFh ; Mask to enable interrupt for modem port
MDINTO2 EQU 0F7h ; Enable interrupt level 3
MDMINTC EQU 010h ; Bit to set to disable interrupts for modem
MDINTC2 EQU 008h ; Disable IRQ3
INTCONT EQU 0021h ; Address of 8259 interrupt controller ICW2-3
INTCON1 EQU 0020h ; Address of 8259 ICW1
EOICOM EQU 0064h ; End of interrupt
EOICOM2 EQU 0063h ; End of interrupt for COM2
; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
; or port2)
; port1, port2 - portinfo structures for the corresponding ports
; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff
; setktab - keyword table for redefining keys (should contain a 0 if
; not implemented)
; setkhlp - help for setktab
DataS SEGMENT PUBLIC 'DataS'
EXTRN drives:byte,flags:byte, trans:byte, PC_Subtype:BYTE
EXTRN portval:word, port1:byte, port2:byte
EXTRN Count:WORD, XofSnt:BYTE, Force_mono:BYTE, Force_card:BYTE
EXTRN Source:BYTE, SrcPnt:WORD, PC_Type:BYTE
PUBLIC Video_page_addresses
Video_page_addresses LABEL WORD
DW 0B800h ; Page zero
DW 0B900h ; Page one
SetKTab_PC DB 12
mkeyw 'BACKSPACE',0eh
mkeyw 'F1',3bh
mkeyw 'F2',3ch
mkeyw 'F3',3dh
mkeyw 'F4',3eh
mkeyw 'F5',3fh
mkeyw 'F6',40h
mkeyw 'F7',41h
mkeyw 'F8',42h
mkeyw 'F9',43h
mkeyw 'F10',44h
mkeyw 'SCAN',-1
SetKHlp_PC DB cr,lf,'Keyname: backspace, f1, ... f10, or "SCAN" followed by '
DB 'decimal scan code$'
brkval DB 0 ; What to send for a break
brkadr DW 0 ; Where to send it
modem mdminfo <MDMDAT1,MDMSTS1,MDMCOM1,MDMINTO,MDMINTC,EOICOM,MDMINTV>
erms20 DB '? Warning: System has no disk drives', Cr, Lf, '$'
erms40 DB '? Warning: Unrecognized baud rate', Cr, Lf, '$'
badbd DB 'Unimplemented baud rate', Cr, Lf, '$'
No_memory DB '? Not enough memory for program to run (screen management)'
DB Cr, Lf, '$'
crlf DB cr,lf,'$'
delstr DB BS,' ',BS,'$' ; Delete string. [21d]
clrlin DB cr,'$' ; Clear line (just the cr part)
savsci DW ? ; Save for serial port interrupt vector. [14]
savscs DW ? ; Ditto. [14]
savbr1 DW ? ; "Break" interrupt vector. [25]
savbr2 DW ? ; Ditto. [25]
portin DB 0 ; Has comm port been initialized. [21c]
xofrcv DB 0 ; Say if we received an XOFF
tmp DB ?,'$'
temp DW 0
temp1 DW ? ; Temporary storage
temp2 DW ? ; Temporary storage
CRT_Mode DB 7 ; Crt mode for video card
Which_page DB 0 ; Video page number
Screen_pointer DW 0B000h ; Pointer to real screen memory
Beam_status DB ? ; Place to hold CRT status reg value
ontab DB 02H ; Two entries
DB 03H,'OFF$' ; Should be alphabetized. [19a]
DW 00H
DB 02H,'ON$'
DW 01H
comptab DB 04H
DB 01H,'1$'
DW 01H
DB 01H,'2$'
DW 00H
DB 04H,'COM1$'
DW 01H
DB 04H,'COM2$'
DW 00H
; this table is indexed by the baud rate definitions given in
; pcdefs. Unsupported baud rates should contain FF
bddat label word
DW 0FFH ; 45.5 baud -- Not supported
DW 900H ; 50 baud
DW 600H ; 75 baud
DW 417H ; 110 baud
DW 359H ; 134.5 baud
DW 300H ; 150 baud
DW 180H ; 300 baud
DW 0C0H ; 600 baud
DW 60H ; 1200 baud
DW 40H ; 1800 baud
DW 3AH ; 2000 baud
DW 30H ; 2400 baud
DW 18H ; 4800 baud
DW 0CH ; 9600 baud
DW 06h ; 19200 baud
DW 03h ; 38400 baud
DW 05h ; 23040 baud
; variables for serial interrupt handler
savesi DW 0 ; Save SI register here
telflg DB 0 ; Are we acting as a terminal
mst DW 0 ; Modem status address
mdat DW 0 ; Modem data address
mdeoi DB 0 ; End-of-Interrupt value
rbtrn DB 7fH ; rubout
shkbuf DB 300 dup (?) ; room for definition
shkmsg DB ' Scan code: '
shkmln EQU $-shkmsg
shkms1 DB cr,lf,' Definition: '
shkm1ln EQU $-shkms1
DataS ENDS
Code SEGMENT PUBLIC
EXTRN Comnd:near, dopar:near, defkey:near, gss:near
EXTRN Set_up_dynamic_memory:NEAR, Exit2:NEAR, NoInt:NEAR, OkInt:NEAR
EXTRN Write_to_standard_output:NEAR
ASSUME cs:Code, ds:DataS
Which_card DB 0 ; 0 for mono, 1 for color, 2 for EGA
; local initialization
LclIni_PC proc near
call Set_up_dynamic_memory ; Try to get memory
jnc LCL_0
mov ah, PrStr ; Code to type message
mov dx, OFFSET No_memory ; Complain about lack of memory
int Dos ; Display it
call Exit2 ; Bomb out also
nop
nop
nop
ret ; Return to main program
LCL_0: mov ax,0eH ; scan code for arrow key
mov si,offset rbtrn ; translate to rubout
mov cx,1 ; one char translation
call defkey
mov brkval,BRKBIT ; What to send for a break
mov ax,modem.mdcom ; Where to send it
mov brkadr,ax
call Set_up_video_card_pointers
jmp Go_to_page_zero
LclIni_PC endp
Set_up_video_card_pointers PROC
mov ah, 15 ; Code to read current video mode
int Screen ; Read it
mov CRT_Mode, al ; Save crt mode
sub ah, ah ; Set flag for mono
mov Which_page, ah ; Zero the page number
cmp Force_card, 0FFh ; Are we forcing some card type?
je SUV_0 ; No, keep checking
mov ah, Force_card ; Pick up forced value
mov Which_card, ah ; Save it
ret ; That's it
SUV_0: cmp al, 7 ; Is it really color?
je SUV_1 ; Ok, it is monochrome
mov ah, 1 ; Flag for color card
SUV_1: mov Which_card, ah ; Remember which card we are using
cmp ah, 1 ; Did we just decide that this is a color card?
je Check_for_EGA ; See if we are on the Enhanced card
ret ; That's it
Set_up_video_card_pointers ENDP
; See if we are using the Enhanced Graphics Adapter
Check_for_EGA PROC
cmp PC_Type, 0 ; We won't even bother without PC-DOS
jne CEG_Done ; Not running PC-DOS, assume not EGA
cmp PC_Subtype, 0 ; Are we on a normal PC?
je CEG_PC ; Yes
cmp PC_Subtype, 1 ; Are we on an XT?
je CEG_XT ; Yes
cmp PC_Subtype, 4 ; ... or a Portable (same motherboard as XT)?
je CEG_XT ; Yes
cmp PC_Subtype, 3 ; Are we on an AT?
je CEG_AT ; Yes
; jmp CEG_Done ; No way of knowing about EGA, assume CGA
jmp CEG_Assume_EGA ; Try default of EGA for non-PC type machines
; What this really means is that we don't
; assume working BIOS support for our "normal"
; complicated color screen update
; This certainly speeds up the screen on
; liquid crystal (and other ...) displays
; Regular PC -- give it a shot!
CEG_PC: call NoInt ; No ints during this
in al, 61h ; This port allows access to port 60h
mov bl, al ; Save the original value from port 61h
or al, 80h ; Turn on the bit making port 60h be switches
out 61h, al ; Set this up
in al, 60h ; Read system board switch bank 1
xchg al, bl ; Save the switch settings, get back original
; value from port 61h
out 61h, al ; Restore original value
call OkInt ; Interrupts are permitted again
mov al, bl ; Get back the switch settings
jmp SHORT CEG_Common_code ; Join common code
; PC/XT -- have to use a slightly different method
CEG_XT: call NoInt ; No interrupts during this stuff
in al, 61h ; This port controls how we read port 62h
mov bl, al ; Save the original value from port 61h
or al, 40h ; Force this bit on so we read the high half
out 61h, al ; Set this up
in al, 62h ; Read the high half of SW 1
xchg al, bl ; Save the switch settings, get back original
; value from port 61h
out 61h, al ; Restore original value
call OkInt ; Interrupts are permitted again
mov al, bl ; Get back the switch settings
jmp SHORT CEG_Common_code ; Join common code
; PC AT, look into CMOS battery powered RAM for configuration info
CEG_AT: mov al, 14h ; CMOS register holding equip flags
out 70h, al ; Tell CMOS chip which register we want
in al, 71h ; Read the equip flags
PUBLIC CEG_Common_code
CEG_Common_code:
test al, 30h ; Are either of these switches set?
jnz CEG_Done ; Not EGA
CEG_Assume_EGA:
mov Which_card, 2 ; Code for EGA
CEG_Done: ret ; Go home
Check_for_EGA ENDP
PUBLIC Switch_video_pages
Switch_video_pages:
mov ax, 0B000h ; Address of monochrome card
cmp Which_card, 0 ; Monochrome card?
je SVP_Set ; Yes
mov ax, 0B800h ; Address of color card
cmp Which_card, 2 ; EGA?
je SVP_Set ; Yes
; True color card, really switch pages
mov al, 1 ; Get a one
sub al, Which_page ; 1 minus 0 = 1, 1 minus 1 = 0
mov Which_page, al ; Store for next time
mov ah, 5 ; Code to switch video pages
int Screen ; Tell ROM BIOS to switch pages now
mov bl, Which_page ; Get number of page we are on
sub bh, bh ; Zero top half of word
shl bx, 1 ; Double for word offset
mov ax, Video_page_addresses[bx] ; Address of the screen memory
SVP_Set:
mov Screen_pointer, ax ; Set up this pointer
ret ; Done here
PUBLIC Go_to_page_zero
Go_to_page_zero:
sub al, al ; Get a zero
mov Which_page, al ; Store it as the page we are on
mov ah, 5 ; Code to switch video pages
int Screen ; Tell ROM BIOS to switch pages now
mov ax, 0B000h ; Address of monochrome card's only page
cmp Which_card, 0 ; Mono card?
je PG0_Mono ; Monochrome
mov ax, 0B800h ; Addr of color card page zero (maybe EGA)
PG0_Mono:
mov Screen_pointer, ax ; Set up pointer
ret
PUBLIC Blank_current_page
Blank_current_page:
mov ax, 600h ; Code to blank entire window
mov bh, 7 ; Normal video attribute
sub cx, cx ; Make a zero as upper left corner
mov dx, (100h * (Number_of_rows_PC - 1) ) + Highest_column
; Use real lower right corner of screen
int Screen ; Ask BIOS to blank it for us
ret ; Done here
; Turn video beam off and on to prevent screen snow on color card
Beam_Off PROC
cmp CRT_Mode, 7 ; BW?
je BOF_1 ; Yes
push es ; Save reg
sub ax, ax ; Make a zero
mov es, ax ; Address low memory
mov al, es:[465h] ; Pick up DOS's value for this
mov Beam_status, al ; Save for later
and al, NOT Video_enable ; Mask off the video bit
mov dx, Mode_control_port ; Address of CRT control port
out dx, al ; Turn off the video now
pop es ; Restore saved reg
BOF_1: ret
Beam_Off ENDP
Beam_On PROC
cmp CRT_Mode, 7 ; BW?
je BON_1 ; Yes
mov al, Beam_status ; Pick up saved beam state
mov dx, Mode_control_port ; Address of CRT control port
out dx, al ; Restore the old state
BON_1: ret
Beam_On ENDP
; this is called by Kermit initialization. It checks the
; number of disks on the system, sets the drives variable
; appropriately. Returns normally
DoDisk_PC PROC NEAR
int mconf ; Get equipment configuration
mov ah,al ; Store AL value for a bit
and al,01H ; First, look at bit 0
jz dodsk0 ; No disk drives -- forget it
mov al,ah ; Get back original value
mov cl,6 ; Shift over bits 6 and 7
shr al,cl ; To positions 0 and 1
inc al ; Want 1 thru 4 (not 0 thru 3)
mov drives,al ; Remember how many
ret
dodsk0: mov ah,prstr ; Print a warning message
mov dx,offset erms20 ; I'm not sure if things will
int dos ; work with only a cassette
mov drives,0 ; Say there aren't any drives
ret
DoDisk_PC ENDP
; show the definition of a key. The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax
; Returns a string to print in AX, length of same in CX
; Returns normally
ShowKey_PC proc near
push es
push ax ; save the ptr
mov bx,ds
mov es,bx ; address data segment
cld
showk1: xor ah,ah
int keyb ; read a char
push ax ; save the character
call gss ; get shift state
pop bx
mov ah,al ; shift state to ah
mov al,bh ; scan code to al
push ax ; remember scan code
mov di,offset shkbuf
mov si,offset shkmsg
mov cx,shkmln
rep movsb ; copy in initial message
call nout ; write out scan code
mov si,offset shkms1
mov cx,shkm1ln ; second message
rep movsb
pop ax ; get scan code back
pop bx ; and terminal arg block
mov cx,[bx].klen ; and length
jcxz showk2 ; no table, not defined
push di ; remember output ptr
mov di,[bx].ktab ; get key table
repne scasw ; search for a definition for this
mov si,di ; remember result ptr
pop di ; get output ptr back
jne showk2 ; not defined, forget it
sub si,[bx].ktab ; compute offset from beginning
sub si,2 ; minus 2 for pre-increment
add si,[bx].krpl ; get index into replacement table
mov si,[si] ; pick up replacement
mov cl,[si] ; get length
mov ch,0
inc si
rep movsb ; copy into buffer
showk2: mov ax,offset shkbuf ; this is buffer
mov cx,di
sub cx,ax ; length
pop es
ret ; and return
ShowKey_PC endp
; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer. This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer
; Returns normally
ClrBuf_PC PROC NEAR
call NoInt ; No interrupts during this
mov ax,offset source
mov srcpnt,ax
mov savesi,ax
mov count,0
call OkInt
ret
ClrBuf_PC ENDP
; Clear to the end of the current line. Returns normally
ClearL_PC PROC NEAR
mov ah,3 ; Clear to end of line
mov bh,0
int bios ; Get current cursor position
mov cx,dx
mov dl,79
mov ah,7
mov al,0
mov bh,7
int bios
ret
ClearL_PC ENDP
; Put the char in AH to the serial port. This assumes the
; port has been initialized. Should honor xon/xoff. Skip returns on
; success, returns normally if the character cannot be written
OutChr_PC:
push cx ; Save initial cx
mov bp,portval
cmp ds:[bp].floflg,0 ; Are we doing flow control
je outch2 ; No, just continue
sti ; Make sure interrupts are ok
mov cx, 4 ; Perform the inner loop this many times
outcha: push cx ; Save outer loop counter
xor cx, cx ; Clear inner loop counter
outch1: cmp xofrcv, true ; Are we being held?
jne outchb ; No - it's OK to go on
loop outch1 ; Held, try for a while (inner loop)
pop cx ; Restore outer loop counter
loop outcha ; Restart inner loop
mov xofrcv, false ; Timed out, force it off and fall thru
jmp SHORT outch2 ; Join common code
outchb: pop cx ; Flush saved outer loop counter
outch2: push dx ; Save register
sub cx,cx
mov al,ah ; Parity routine works on AL
call dopar ; Set parity appropriately
mov ah,al ; Don't overwrite character with status
mov dx,modem.mdstat ; Get port status
outch3: in al,dx
test al,20H ; Transmitter ready?
jnz outch4 ; Yes
loop outch3
jmp outch5 ; Timeout
outch4: mov al,ah ; Now send it out
mov dx,modem.mddat
out dx,al
pop dx
pop cx
jmp rskp
outch5: pop dx
pop cx
ret
; This routine blanks the screen. Returns normally
CmBlnk_PC PROC NEAR ; This is stolen from the IBM example
mov cx,0
mov dx,184FH
mov bh,7
mov ax,600H
int bios
ret
CmBlnk_PC ENDP
; Locate_PC: homes the cursor. Returns normally
Locate_PC PROC NEAR
mov dx,0 ; Go to top left corner of screen
jmp PosCur_PC
Locate_PC ENDP
; Write a line in inverse video (or Blue-on-White) at the
; bottom of the screen... Ptr to the line is passed in dx,
; line is terminated by a $. Returns normally
PutMod_PC PROC
mov bh,70h ; Inverse video
cmp Force_mono, 0 ; Mono forced?
jne PMD_1 ; Yes
cmp Which_card, 0 ; Monochrome card?
je PMD_1 ; Yes
mov bh, 71h ; Use Blue on White on color card
PMD_1: jmp Mode_common ; Use common code
PutMod_PC ENDP
; Write a line in inverse video or Red-on-White at the
; bottom of the screen... Ptr to line is passed in dx,
; line is terminated by a $. Returns normally
PutErr_PC PROC
mov bh,70h ; Inverse video
cmp Force_mono, 0 ; Mono forced?
jne PER_1 ; Yes
cmp Which_card, 0 ; Monochrome card?
je PER_1 ; Yes
mov bh, 74h ; Use Red on White on color card
PER_1: jmp Mode_common ; Join common code
PutErr_PC ENDP
; Common code for PutMod_PC and PutErr_PC
Mode_common PROC
push dx ; preserve message
mov cx,1800h
mov dx,184fh
mov ax,600h ; scroll to clear the line
int bios ; Do it
mov dx,1800h ; now address line 24
call PosCur_PC
pop dx ; get message back
call Write_to_standard_output ; Use standard routine to do this
; mov ah,prstr
; int dos ; write it out
ret ; and return
Mode_common ENDP
; clear the mode line written by PutMod_PC. Returns normally
ClrMod_PC proc near
mov cx,1800h
mov dx,184fh
mov ax,600h
mov bh,7h
int bios
ret
ClrMod_PC endp
; put a help message on the screen. This one uses reverse video..
; pass the message in ax, terminated by a null. Returns normally
PutHlp_PC proc near
push ax ; preserve this
cld ; Forwards
mov si,ax ; point to it
mov dh,0 ; init counter
puthl1: lodsb ; get a byte
cmp al,lf ; linefeed?
jne puthl2 ; no, keep going
inc dh ; count it
jmp puthl1 ; and keep looping
puthl2: cmp al,0 ; end of string?
jne puthl1 ; no, keep going
mov ax,600h ; scroll to clear window
xor cx,cx ; from top left
mov dl,4fh ; to bottom right of needed piece
mov bh, 70h ; Inverse video
cmp Force_mono, 0 ; Mono forced?
jne PHP_1 ; Yes
cmp Which_card, 0 ; Monochrome card?
je PHP_1 ; Yes
mov bh, 1Eh ; Use Yellow on Blue on color card
PHP_1: int bios
call Locate_PC ; home cursor
pop si ; point to string again
puthl3: lodsb ; get a byte
cmp al,0 ; end of string?
je puthl4 ; yes, stop
mov ah,14
int bios ; else write to screen
jmp puthl3 ; and keep going
puthl4: call ClrMod_PC ; Erase any mode line already there
mov dx,24 * 100H ; go to last line
jmp PosCur_PC ; position and return
PutHlp_PC endp
%OUT >> About half way through source file
; Set the baud rate for the current port, based on the value
; in the portinfo structure. Returns normally
DoBaud_PC PROC NEAR
mov bp,portval
mov temp1,ax ; Don't overwrite previous rate. [25]
mov ax,ds:[bp].baud ; Check if new rate is valid. [25]
mov tmp,2
mul tmp ; Get index into baud table
mov bx,offset bddat ; Start of table
add bx,ax
mov ax,[bx] ; The data to output to port
cmp ax,0FFH ; Unimplemented baud rate
jne dobd0
mov ax,temp1 ; Get back orginal value
mov ds:[bp].baud,ax ; Leave baud rate as is
mov ah,prstr
mov dx,offset badbd ; Give an error message
int dos
ret
dobd0: mov temp1,ax ; Remember value to output. [25]
mov dx,modem.mdcom ; LCR -- Initialize baud rate. [19b]
in al,dx
mov bl,al
or ax,80H
out dx,al
mov dx,modem.mddat ; [19b]
mov ax,temp1
out dx,al
inc dx
mov al,ah
out dx,al
mov dx,modem.mdcom ; [19b]
mov al,bl
out dx,al
ret
DoBaud_PC ENDP
; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port. Returns normally
; This is used during initialization
GetBaud_PC PROC NEAR
mov dx,modem.mdcom ; Get current Line Control Register value
in al,dx
mov bl,al ; Save it
or ax,80H ; Turn on to access baud rate generator
out dx,al
mov dx,modem.mddat ; Divisor latch
inc dx
in al,dx ; Get hi order byte
mov ah,al ; Save here
dec dx
in al,dx ; Get lo order byte
push ax
mov dx,modem.mdcom
mov al,bl ; Restore old value
out dx,al
pop ax
cmp ax,0FFFFH ; Who knows what this is
je getb2
mov bx,offset bddat ; Find rate's offset into table
mov cl,0 ; Keep track of index
getb0: cmp ax,[bx]
je getb1
inc cl
cmp cl,baudsiz ; At the end of the list
jge getb2
add bx,2
jmp getb0
getb1: mov ch,0
mov bp,portval
mov ds:[bp].baud,cx ; Set baud rate
ret
getb2: mov ah,prstr
mov dx,offset erms40
int dos
ret
GetBaud_PC ENDP
; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx
PrtChr_PC PROC NEAR
cld ; Forwards
call chkxon ; see if we need to xon
cmp count,0
jnz prtch2
jmp rskp ; No data - check console
prtch2: mov si,savesi
lodsb ; get a byte
cmp si,offset source + bufsiz ; bigger than buffer?
jb prtch1 ; no, keep going
mov si,offset source ; yes, wrap around
prtch1: dec count
mov savesi,si
mov dx,count ; return # of chars in buffer
ret
PrtChr_PC ENDP
; local routine to see if we have to transmit an xon
chkxon proc near
push bx
mov bx,portval
cmp [bx].floflg,0 ; doing flow control?
je chkxo1 ; no, skip all this
cmp xofsnt,false ; have we sent an xoff?
je chkxo1 ; no, forget it
cmp count,XOFF_trigger_count ; below trigger?
jae chkxo1 ; no, forget it
mov ax,[bx].flowc ; ah gets xon
call OutChr_PC ; send it
nop
nop
nop ; in case it skips
mov xofsnt,false ; remember we've sent the xon
chkxo1: pop bx ; restore register
ret ; and return
chkxon endp
; Send a break out the current serial port. Returns normally
SENDBR_PC PROC
push cx
push dx
push ax
xor cx,cx ; Clear loop counter
mov dx,brkadr ; Port address. [19b]
in al,dx ; Get current setting
or al,brkval ; Set send-break bit(s)
out dx,al ; Start the break
push ax
mov ax,275 ; # of ms to wait
call pcwait ; hold break for desired interval
pop ax
xor al,brkval ; Clear send-break bit(s)
out dx,al ; Stop the break
pop ax
pop dx
pop cx
ret ; And return
SENDBR_PC ENDP
; Wait for the # of milliseconds in ax
; Thanks to Bernie Eiben for this one
pcwait proc near
mov cx,240 ; inner loop counter for 1 millisecond
cmp PC_Subtype, 3 ; Is this an AT?
jne pcwai1 ; No
mov cx, 4*240 ; It is an AT, use a much higher value
pcwai1: sub cx,1 ; inner loop takes 20 clock cycles
jnz pcwai1
dec ax ; outer loop counter
jnz pcwait ; wait another millisecond
ret
pcwait endp
; Position the cursor according to contents of DX:
; DH contains row, DL contains column. Returns normally
PosCur_PC PROC NEAR
mov ah,2 ; Position cursor
mov bh,0
int bios
ret
PosCur_PC ENDP
; Delete a character from the terminal. This works by printing
; backspaces and spaces. Returns normally
DoDel_PC PROC NEAR
mov ah,prstr
mov dx,offset delstr ; Erase weird character
int dos
ret
DoDel_PC ENDP
; Move the cursor to the left margin, then clear to end of line
; Returns normally
CtlU_PC PROC NEAR
mov ah,prstr
mov dx,offset clrlin
int dos
call ClearL_PC
ret
CtlU_PC ENDP
; set the current port
Coms_PC PROC NEAR
mov dx,offset comptab
mov bx,0
mov ah,cmkey
call comnd
jmp r
push bx
mov ah,cmcfm
call comnd ; Get a confirm
jmp comx ; Didn't get a confirm
nop
pop bx
mov flags.comflg,bl ; Set the comm port flag
cmp flags.comflg,1 ; Using Com 1?
jne coms0 ; Nope
mov ax,offset port1
mov portval,ax
mov modem.mddat,MDMDAT1 ; Set COM1 defaults
mov modem.mdstat,MDMSTS1
mov modem.mdcom,MDMCOM1
mov modem.mddis,MDMINTC
mov modem.mden,MDMINTO
mov modem.mdmeoi,EOICOM
mov modem.mdintv,MDMINTV
mov brkadr,MDMCOM1
ret
coms0: mov ax,offset port2
mov portval,ax
mov modem.mddat,MDMDAT2 ; Set COM2 defaults
mov modem.mdstat,MDMSTS2
mov modem.mdcom,MDMCOM2
mov modem.mddis,MDINTC2
mov modem.mden,MDINTO2
mov modem.mdmeoi,EOICOM2
mov modem.mdintv,MDINTV2
mov brkadr,MDMCOM2
ret
comx: pop bx
ret
Coms_PC ENDP
; Set heath emulation on/off
VTS_PC PROC NEAR
mov dx,offset ontab
mov bx,0
mov ah,cmkey
call comnd
jmp r
push bx
mov ah,cmcfm
call comnd ; Get a confirm
jmp vt0 ; Didn't get a confirm
nop
pop bx
mov flags.vtflg,bl ; Set the VT52 emulation flag
ret
vt0: pop bx
ret
VTS_PC ENDP
; initialization for using serial port. This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done)
; SerRst_PC below should restore any interrupt vectors that this changes
; Returns normally
SerIni_PC PROC
push es
cmp portin, 0 ; Did we initialize port already? [21c]
jne serin0 ; Yes, so just leave. [21c]
call NoInt ; Disable interrupts
xor ax,ax ; Address low memory
mov es,ax
mov bx,modem.mdintv ; Save serial card interrupt vector. [19b]
mov ax,es:[bx]
mov savsci,ax
mov ax,offset SerInt ; And point it to my routine
mov es:[bx],ax
add bx,2 ; Save CS register too. [19b]
mov ax,es:[bx]
mov savscs,ax
mov es:[bx],cs
mov portin,1 ; Remember port has been initialized
call ClrBuf_PC ; Clear input buffer
mov ax,modem.mdstat
mov mst,ax ; Use this address for status
mov ax,modem.mddat
mov mdat,ax ; Use this address for data
mov al,modem.mdmeoi
mov mdeoi,al ; Use to signify end-of-interrupt
in al,21H ; Set up 8259 interrupt controller
and al,modem.mden ; Enable INT3 or INT4
out 21H,al
mov dx,modem.mdcom ; Set up the serial card
mov al,3
out dx,al
mov dl,0F9h
mov al,1 ; Set up interrupt enable register
out dx,al
mov dl,0FCh ; Enable interrupts from serial card
mov al,0Bh
out dx,al
call OkInt ; Allow interrupts
mov dl,0F8h
in al,dx
serin0: pop es
ret
SerIni_PC ENDP
; Reset the serial port. This is the opposite of SerIni_PC. Calling
; this twice without intervening calls to SerIni_PC should be harmless.
; Returns normally.
SerRst_PC PROC
push es ; Preserve this
cmp portin,0 ; Reset already?
je srst1 ; Yes, just leave
call NoInt ; Disable interrupts
mov dx, 03FCh ; Disable modem interrupts, assume port 1
cmp flags.comflg, 1 ; Is it port 1?
je srst0 ; Yes
mov dh, 2 ; Port 2
srst0: mov al, 3
out dx,al
in al, 21h ; Interrupt controller
or al, modem.mddis ; Inhibit IRQ3 or IRQ4
out 21H, al
xor bx, bx ; Address low memory
mov es, bx
mov bx, modem.mdintv ; Restore the serial card int vector
mov ax, savsci
mov es:[bx], ax
add bx, 2 ; Restore CS too
mov ax, savscs
mov es:[bx], ax
mov portin, 0 ; Reset flag
call OkInt
srst1: pop es
ret ; All done
SerRst_PC ENDP
Drop_DTR_PC PROC
push ax ; Save regs
push dx
mov dx, 03FCh ; Port for COM1 modem control register
cmp flags.comflg, 1 ; Using port 1 ?
je DTR_0 ; Yes, continue
mov dh, 2 ; Using COM2
DTR_0: sub al, al ; Clear this register
out dx,al ; Drop DTR now
pop dx ; Restore regs
pop ax
ret ; Done here
Drop_DTR_PC ENDP
; Serial port interrupt routine
SERINT PROC FAR
; Save some registers ...
push dx
push ds
push ax
push es
mov ax, SEG DataS ; Address data segment
mov ds, ax ; Set up ds
mov es, ax ; and es
mov dx, mst ; Line Status Register (LSR), for port 1 or 2
in al, dx
test al, mdminp ; Data available?
jz Return_2 ; Nope
mov dx, mdat ; Get the data
in al,dx
cmp Count, BufSiz ; Room left in buffer?
jae Return_2 ; No room left, don't overstore!!
; Save more registers ...
push bx
push di
push bp
mov bp, portval
cmp ds:[bp].floflg, 0 ; Doing flow control?
je srint2 ; Nope
mov bx, ds:[bp].flowc ; Flow control char (BH = XON, BL = XOFF)
cmp al, bl ; Is it an XOFF?
jne srint1 ; Nope, go on
mov xofrcv, true ; Set the flag
jmp SHORT RetInt
srint1: cmp al, bh ; Get an XON?
jne srint2 ; No, go on
mov xofrcv, false ; Clear our flag
; jmp SHORT RetInt
RetInt:
; Restore some saved registers ...
pop bp
pop di
pop bx
Return_2:
sti ; Allow ints now
mov al, mdeoi ; Send End-of-Interrupt ...
out intcon1, al ; to 8259
; Restore other saved registers ...
pop es
pop ax
pop ds
pop dx
iret ; Return from interrupt
srint2: cld ; Store forwards
mov di, srcpnt ; Where to store it
stosb ; Store it
cmp di, OFFSET Source + BufSiz
jb srint3 ; Not past end ...
mov di, OFFSET Source ; Wrap buffer around
srint3: inc count ; Bump the count
mov srcpnt, di ; Store the updated pointer
cmp ds:[bp].floflg, 0 ; Doing flow control?
je RetInt ; No, just leave
cmp count, XOFF_trigger_count ; Past the high trigger point?
jbe RetInt ; No, we're within our limit
cmp xofsnt, true ; Have we sent an XOFF?
je RetInt ; Yes
mov ah,bl ; Get the XOFF
push cx ; OutChr_PC clobbers cx, we're not allowed to
call OutChr_PC ; Send it
nop
nop
nop
pop cx ; Restore cx
mov xofsnt,true ; Remember we sent it
jmp SHORT RetInt ; Branch upwards into common code
SERINT ENDP
; Produce a short beep. The PC DOS bell is long enough to cause a loss
; of data at the port. Returns normally
Beep_PC PROC NEAR
mov al,10110110B ; Gen a short beep (long one losses data.)
out timer+3,al ; Code snarfed from Technical Reference
mov ax,533H
out timer+2,al
mov al,ah
out timer+2,al
in al,port_b
mov ah,al
or al,03
out port_b,al
sub cx,cx
mov bl,1
beep0: loop beep0
dec bl
jnz beep0
mov al,ah
out port_b,al
ret
Beep_PC ENDP
; put the number in ax into the buffer pointed to by di. Di is updated
nout proc near
cld ; Forwards
mov dx,0 ; high order is always 0
mov bx,10
div bx ; divide to get digit
push dx ; save remainder digit
or ax,ax ; test quotient
jz nout1 ; zero, no more of number
call nout ; else call for rest of number
nout1: pop ax ; get digit back
add al,'0' ; make printable
stosb ; drop it off
ret ; and return
nout endp
; Jumping to this location is like retskp. It assumes the instruction
; after the call is a jmp addr
RSKP PROC NEAR
pop bp
add bp,3
push bp
; ret
RSKP ENDP
; Jumping here is the same as a ret
R PROC NEAR
ret
R ENDP
Code ENDS
END